//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "ImageLoader.h"

namespace Engine
{
namespace Loaders
{

	bool LoadTexture1D(IFilePtr pFile, TImage1D &sImage, bool bReadData = true);
	bool LoadTBO(IFilePtr pFile, TImage1D &sImage, bool bReadData = true);
	bool LoadTexture2D(IFilePtr pFile, TImage2D &sImage, bool bReadData = true);
	bool LoadCubeMap(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData = true);
	bool LoadTexture2DArray(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData = true);
	bool LoadCubeMapArray(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData = true);
	bool LoadTexture3D(IFilePtr pFile, TImage3D &sImage, bool bReadData = true);

	
	bool GetImageType(IFilePtr pFile, e_image::type &eType)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	a_header[16]	= {0};
		char	*p_image_type	= a_header + 12;

		CHECK_RETURN( pFile->ReadS( a_header, sizeof(a_header) ) );
		
		a_header[11] = '\0';
		a_header[15] = '\0';

		CHECK_VALUE( strcmp( a_header, s_aTexFileHeader ) == 0 );

		if ( strcmp( p_image_type, "T1D" ) == 0 )	eType = e_image::TEXTURE_1D;		else
		if ( strcmp( p_image_type, "T1A" ) == 0 )	eType = e_image::TEXTURE_1D_ARRAY;	else
		if ( strcmp( p_image_type, "TBO" ) == 0 )	eType = e_image::TEXTURE_BUFFER;	else
		if ( strcmp( p_image_type, "T2D" ) == 0 )	eType = e_image::TEXTURE_2D;		else
		if ( strcmp( p_image_type, "T2A" ) == 0 )	eType = e_image::TEXTURE_2D_ARRAY;	else
		if ( strcmp( p_image_type, "T3D" ) == 0 )	eType = e_image::TEXTURE_3D;		else
		if ( strcmp( p_image_type, "VOL" ) == 0 )	eType = e_image::TEXTURE_3D;		else
		if ( strcmp( p_image_type, "CBM" ) == 0 )	eType = e_image::CUBE_MAP;			else
		if ( strcmp( p_image_type, "CMA" ) == 0 )	eType = e_image::CUBEMAP_ARRAY;		else
			RETURN_ERROR( "not suitable texture type" );

		return true;
	}
	

	inline void CopyInfo(const TImage1D &sImage, TImageInfo &sInfo)
	{
		sInfo.eFormat		= sImage.eFormat;
		sInfo.eSwizzle		= sImage.eSwizzle;
		sInfo.vDimension	= uvec3( sImage.uLength, (uint)0, (uint)0 );
		sInfo.bCompressed	= sImage.bCompressed;
	}


	inline void CopyInfo(const TImage2D &sImage, TImageInfo &sInfo)
	{
		sInfo.eFormat		= sImage.eFormat;
		sInfo.eSwizzle		= sImage.eSwizzle;
		sInfo.vDimension	= uvec3( sImage.vTexDim );
		sInfo.bCompressed	= sImage.bCompressed;
	}


	inline void CopyInfo(const TImage3D &sImage, TImageInfo &sInfo)
	{
		sInfo.eFormat		= sImage.eFormat;
		sInfo.eSwizzle		= sImage.eSwizzle;
		sInfo.vDimension	= sImage.vTexDim;
		sInfo.bCompressed	= sImage.bCompressed;
	}


	inline void CopyInfo(const array<TImage2D> &aImages, TImageInfo &sInfo)
	{
		sInfo.eFormat		= aImages[0].eFormat;
		sInfo.eSwizzle		= aImages[0].eSwizzle;
		sInfo.vDimension	= uvec3( aImages[0].vTexDim, aImages.Count() );
		sInfo.bCompressed	= aImages[0].bCompressed;
	}


	bool GetImageInfo(IFilePtr pFile, TImageInfo &sInfo)
	{
		char	ac_type[12]		= {0},
				ac_tex_type[4]	= {0};

		CHECK_RETURN( pFile->ReadS( ac_type, sizeof(ac_type) ) );
		ac_type[11] = '\0';

		CHECK_VALUE( strcmp( ac_type, s_aTexFileHeader ) == 0 );

		CHECK_RETURN( pFile->ReadS( ac_tex_type, sizeof(ac_tex_type) ) );
		ac_tex_type[3] = '\0';

		if ( strcmp( ac_tex_type, "T1D" ) == 0 )
		{
			TImage1D	s_image;
			CHECK_RETURN( LoadTexture1D( pFile, s_image, false ) );

			CopyInfo( s_image, sInfo );
			sInfo.eType = e_image::TEXTURE_1D;
		}
		else

		if ( strcmp( ac_tex_type, "TBO" ) == 0 )
		{
			TImage1D	s_image;
			CHECK_RETURN( LoadTBO( pFile, s_image, false ) );

			CopyInfo( s_image, sInfo );
			sInfo.eType = e_image::TEXTURE_BUFFER;
		}
		else
		
		if ( strcmp( ac_tex_type, "T2D" ) == 0 )
		{
			TImage2D	s_image;
			CHECK_RETURN( LoadTexture2D( pFile, s_image, false ) );

			CopyInfo( s_image, sInfo );
			sInfo.eType = e_image::TEXTURE_2D;
		}
		else

		if ( strcmp( ac_tex_type, "T3D" ) == 0 || strcmp( ac_tex_type, "VOL" ) == 0 )
		{
			TImage3D	s_image;
			CHECK_RETURN( LoadTexture3D( pFile, s_image, false ) );

			CopyInfo( s_image, sInfo );
			sInfo.eType = e_image::TEXTURE_3D;
		}
		else

		if ( strcmp( ac_tex_type, "CBM" ) == 0 )
		{
			array<TImage2D>	a_images;
			CHECK_RETURN( LoadCubeMap( pFile, a_images, false ) );

			CopyInfo( a_images, sInfo );
			sInfo.eType = e_image::CUBE_MAP;
		}
		else

		if ( strcmp( ac_tex_type, "T2A" ) == 0 )
		{
			array<TImage2D>	a_images;
			CHECK_RETURN( LoadTexture2DArray( pFile, a_images, false ) );

			CopyInfo( a_images, sInfo );
			sInfo.eType = e_image::TEXTURE_2D_ARRAY;
		}
		else

		if ( strcmp( ac_tex_type, "CMA" ) == 0 )
		{
			array<TImage2D>	a_images;
			CHECK_RETURN( LoadCubeMapArray( pFile, a_images, false ) );

			CopyInfo( a_images, sInfo );
			sInfo.eType = e_image::CUBEMAP_ARRAY;
		}
		else
			RETURN_ERROR( "not suitable texture type" );

		return true;
	}


	bool LoadImage1D(IFilePtr pFile, TImage1D &sImage)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	ac_type[12]		= {0},
				ac_tex_type[4]	= {0};

		CHECK_RETURN( pFile->ReadS( ac_type, sizeof(ac_type) ) );
		ac_type[11] = '\0';

		CHECK_ERROR( strcmp( ac_type, s_aTexFileHeader ) == 0,
			"Invalid file header \"" << pFile->GetName() << '"'
		);

		CHECK_RETURN( pFile->ReadS( ac_tex_type, sizeof(ac_tex_type) ) );
		ac_tex_type[3] = '\0';

		bool	ret = true;

		if ( strcmp( ac_tex_type, "T1D" ) == 0 )	ret = LoadTexture1D( pFile, sImage, true );	else
		if ( strcmp( ac_tex_type, "TBO" ) == 0 )	ret = LoadTBO( pFile, sImage, true );		else
			RETURN_ERROR( "not suitable texture type" );

		CHECK_ERROR( ret,
			"Can't read texture file data \"" << pFile->GetName() << '"'
		);

		return true;
	}


	bool LoadImage2D(IFilePtr pFile, TImage2D &sImage)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	ac_type[12]		= {0},
				ac_tex_type[4]	= {0};

		CHECK_RETURN( pFile->ReadS( ac_type, sizeof(ac_type) ) );
		ac_type[11] = '\0';
		
		CHECK_ERROR( strcmp( ac_type, s_aTexFileHeader ) == 0,
			"Invalid file header \"" << pFile->GetName() << '"'
		);

		CHECK_RETURN( pFile->ReadS( ac_tex_type, sizeof(ac_tex_type) ) );
		ac_tex_type[3] = '\0';

		bool	ret = false;

		if ( strcmp( ac_tex_type, "T2D" ) == 0 )	ret = LoadTexture2D( pFile, sImage, true );	else
			RETURN_ERROR( "not suitable texture type" );
		
		CHECK_ERROR( ret,
			"can't read texture file data \"" << pFile->GetName() << '"'
		);

		return true;
	}


	bool LoadImage3D(IFilePtr pFile, TImage3D &sImage)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	ac_type[12]		= {0},
				ac_tex_type[4]	= {0};

		CHECK_RETURN( pFile->ReadS( ac_type, sizeof(ac_type) ) );
		ac_type[11] = '\0';
		
		CHECK_ERROR( strcmp( ac_type, s_aTexFileHeader ) == 0,
			"Invalid file header \"" << pFile->GetName() << '"'
		);

		CHECK_RETURN( pFile->ReadS( ac_tex_type, sizeof(ac_tex_type) ) );
		ac_tex_type[3] = '\0';

		bool	ret = false;

		if ( strcmp( ac_tex_type, "T3D" ) == 0 || strcmp( ac_tex_type, "VOL" ) == 0 )
			ret = LoadTexture3D( pFile, sImage, true );
		else
			RETURN_ERROR( "not suitable texture type" );
		
		CHECK_ERROR( ret,
			"Can't read texture file data \"" << pFile->GetName() << '"'
		);

		return true;
	}


	bool LoadImageArray2D(IFilePtr pFile, array<TImage2D> &aImages)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	ac_type[12]		= {0},
				ac_tex_type[4]	= {0};

		CHECK_RETURN( pFile->ReadS( ac_type, sizeof(ac_type) ) );
		ac_type[11] = '\0';
		
		CHECK_ERROR( strcmp( ac_type, s_aTexFileHeader ) == 0,
			"Invalid file header \"" << pFile->GetName() << '"'
		);

		CHECK_RETURN( pFile->ReadS( ac_tex_type, sizeof(ac_tex_type) ) );
		ac_tex_type[3] = '\0';

		bool	ret = false;

		if ( strcmp( ac_tex_type, "CBM" ) == 0 )	ret = LoadCubeMap( pFile, aImages, true );			else
		if ( strcmp( ac_tex_type, "T2A" ) == 0 )	ret = LoadTexture2DArray( pFile, aImages, true );	else
		if ( strcmp( ac_tex_type, "CMA" ) == 0 )	ret = LoadCubeMapArray( pFile, aImages, true );		else
			RETURN_ERROR( "not suitable texture type" );
		
		CHECK_ERROR( ret, "Can't read texture file data \"" << pFile->GetName() << '"' );
		return true;
	}
	

	bool LoadSprite2D(IFilePtr pFile, TSprite2D &sSprite)
	{
		CHECK_ARGUMENT( pFile != nullptr );

		char	ac_type[12]	= {0};
		bool	b_read		= true;

		b_read &= pFile->ReadS( ac_type, sizeof(ac_type) );
		ac_type[11] = '\0';
		
		CHECK_ERROR( strcmp( ac_type, s_aTexFileHeader ) == 0,
			"Invalid file header \"" << pFile->GetName() << '"'
		);

		TFileHeader			s_file_header;
		TSprite2DHeader		s_header;
		uint				temp		 = 0;

		b_read &= pFile->ReadS( &s_file_header,	sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header,		sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );
	
		sSprite.aTexcoords.Resize( s_header.uRectCount, false );
		sSprite.aFrames.Resize( s_header.uIndicesCount, false );
		sSprite.aAnimation.Resize( s_header.uAnimationsCount, false );

		b_read &= pFile->ReadS( sSprite.aTexcoords.ptr(),	sSprite.aTexcoords.Size() );
		b_read &= pFile->ReadS( sSprite.aFrames.ptr(),		sSprite.aFrames.Size() );

		for (uint i = 0; i < sSprite.aAnimation.Count(); ++i)
		{
			TSprAnimHeader	s_anim;

			b_read &= pFile->ReadS( &s_anim, sizeof(s_anim) );
			s_anim.aName[31] = '\0';

			sSprite.aAnimation[i].sName		= s_anim.aName;
			sSprite.aAnimation[i].uCount	= s_anim.uCount;
			sSprite.aAnimation[i].uOffset	= s_anim.uOffset;
		}

		b_read &= LoadImage2D( pFile, sSprite.sImage );
		
		CHECK_ERROR( b_read, "Can't read sprite file data \"" << pFile->GetName() << '"' );
		return true;
	}


	bool LoadTexture1D(IFilePtr pFile, TImage1D &sImage, bool bReadData)
	{
		TFileHeader			s_file_header;
		TTexture1DHeader	s_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;

		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], sImage.eFormat );

		sImage.eSwizzle		= s_header.eSwizzle;
		sImage.uLength		= s_header.uLength;
		sImage.bCompressed	= s_header.bCompress;

		if ( !bReadData )
			return b_read;

		sImage.aMipMaps.Resize( s_header.uMipMapsCount, false );
		sImage.aData.Resize( s_header.uDataSize, false );

		b_read &= pFile->ReadS( sImage.aMipMaps.ptr(), (uint)sImage.aMipMaps.Size() );
		b_read &= pFile->ReadS( sImage.aData.ptr(), (uint)sImage.aData.Size() );

		return b_read;
	}


	bool LoadTBO(IFilePtr pFile, TImage1D &sImage, bool bReadData)
	{
		TFileHeader		s_file_header;
		TTBOHeader		s_header;
		bool			b_read = true;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		sImage.aMipMaps.Resize( 1, false );
		sImage.aData.Resize( s_header.uDataSize, false );
		
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], sImage.eFormat );

		sImage.bCompressed	= false;
		sImage.eSwizzle		= e_tex_swizzle::AUTO;
		sImage.uLength		= 0;

		if ( !bReadData )
			return b_read;

		b_read &= pFile->ReadS( sImage.aData.ptr(), (uint)sImage.aData.Size() );
		return b_read;
	}


	bool LoadTexture2D(IFilePtr pFile, TImage2D &sImage, bool bReadData)
	{
		TFileHeader			s_file_header;
		TTexture2DHeader	s_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;
		
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], sImage.eFormat );

		sImage.eSwizzle		= s_header.eSwizzle;
		sImage.vTexDim		= uvec2( s_header.uWidth, s_header.uHeight );
		sImage.bCompressed	= s_header.bCompress;

		if ( !bReadData )
			return b_read;

		sImage.aMipMaps.Resize( s_header.uMipMapsCount, false );
		sImage.aData.Resize( s_header.uDataSize, false );

		b_read &= pFile->ReadS( sImage.aMipMaps.ptr(), (uint)sImage.aMipMaps.Size() );
		b_read &= pFile->ReadS( sImage.aData.ptr(), (uint)sImage.aData.Size() );

		return b_read;
	}


	bool LoadCubeMap(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData)
	{
		TFileHeader			s_file_header;
		TTexture2DHeader	s_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;

		aImages.Resize( 6, false );

		e_tex_format::type	e_img_fmt = e_tex_format::UNKNOWN;
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], e_img_fmt );

		for (uint i = 0; i < 6; ++i)
		{
			aImages[i].eFormat		= e_img_fmt;
			aImages[i].eSwizzle		= s_header.eSwizzle;
			aImages[i].vTexDim		= uvec2( s_header.uWidth, s_header.uHeight );
			aImages[i].bCompressed	= s_header.bCompress;
			
			if ( !bReadData )
				return b_read;
			
			aImages[i].aMipMaps.Resize( s_header.uMipMapsCount, false );
			aImages[i].aData.Resize( s_header.uDataSize, false );

			b_read &= pFile->ReadS( aImages[i].aMipMaps.ptr(), (uint)aImages[i].aMipMaps.Size() );
			b_read &= pFile->ReadS( aImages[i].aData.ptr(), (uint)aImages[i].aData.Size() );
		}

		return b_read;
	}


	bool LoadTexture2DArray(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData)
	{
		TFileHeader			s_file_header;
		bool				b_read = true;
		uint				u_count	= 0;
		TTexture2DHeader	s_header;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );
	
		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) + sizeof(u_count) );

		b_read &= pFile->ReadS( &u_count,		sizeof(u_count) );
		b_read &= pFile->ReadS( &s_header,		sizeof(s_header) );
	
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		aImages.Resize( u_count, false );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;
		
		e_tex_format::type	e_img_fmt = e_tex_format::UNKNOWN;
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], e_img_fmt );

		for (uint i = 0; i < u_count; ++i)
		{
			aImages[i].eFormat		= e_img_fmt;
			aImages[i].eSwizzle		= s_header.eSwizzle;
			aImages[i].vTexDim		= uvec2( s_header.uWidth, s_header.uHeight );
			aImages[i].bCompressed	= s_header.bCompress;

			if ( !bReadData )
				return b_read;
			
			aImages[i].aMipMaps.Resize( s_header.uMipMapsCount, false );
			aImages[i].aData.Resize( s_header.uDataSize, false );

			b_read &= pFile->ReadS( aImages[i].aMipMaps.ptr(),	(uint)aImages[i].aMipMaps.Size() );
			b_read &= pFile->ReadS( aImages[i].aData.ptr(),		(uint)aImages[i].aData.Size() );
		}

		return b_read;
	}


	bool LoadCubeMapArray(IFilePtr pFile, array<TImage2D> &aImages, bool bReadData)
	{
		TFileHeader			s_file_header;
		bool				b_read = true;
		uint				u_count	= 0;
		TTexture2DHeader	s_header;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );
	
		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) + sizeof(u_count) );

		b_read &= pFile->ReadS( &u_count,		sizeof(u_count) );
		b_read &= pFile->ReadS( &s_header,		sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		u_count *= 6;
		aImages.Resize( u_count, false );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;
		
		e_tex_format::type	e_img_fmt = e_tex_format::UNKNOWN;
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], e_img_fmt );

		for (uint i = 0; i < u_count; ++i)
		{
			aImages[i].eFormat		= e_img_fmt;
			aImages[i].eSwizzle		= s_header.eSwizzle;
			aImages[i].vTexDim		= uvec2( s_header.uWidth, s_header.uHeight );
			aImages[i].bCompressed	= s_header.bCompress;

			if ( !bReadData )
				return b_read;

			aImages[i].aMipMaps.Resize( s_header.uMipMapsCount, false );
			aImages[i].aData.Resize( s_header.uDataSize, false );

			b_read &= pFile->ReadS( aImages[i].aMipMaps.ptr(),	(uint)aImages[i].aMipMaps.Size() );
			b_read &= pFile->ReadS( aImages[i].aData.ptr(),		(uint)aImages[i].aData.Size() );
		}

		return b_read;
	}


	bool LoadTexture3D(IFilePtr pFile, TImage3D &sImage, bool bReadData)
	{
		TFileHeader			s_file_header;
		TTexture3DHeader	s_header;
		bool				b_read = true;

		b_read &= pFile->ReadS( &s_file_header, sizeof(s_file_header) );

		CHECK_VALUE( s_file_header.uHeaderSize == sizeof(s_header) );

		b_read &= pFile->ReadS( &s_header, sizeof(s_header) );
		b_read &= pFile->SeekS( s_file_header.uUserDataSize, e_seek::CURRENT );

		if ( s_header.uMipMapsCount == 0 )
			s_header.uMipMapsCount = 1;
		
		e_tex_format::type	e_img_fmt = e_tex_format::UNKNOWN;
		b_read &= Image::DeconvertFormat( s_header.eFormat[0], s_header.eFormat[1], s_header.eFormat[2], e_img_fmt );

		sImage.eSwizzle		= s_header.eSwizzle;
		sImage.vTexDim		= uvec3( s_header.uWidth, s_header.uHeight, s_header.uDepth );
		sImage.bCompressed	= s_header.bCompress;

		if ( !bReadData )
			return b_read;

		sImage.aMipMaps.Resize( s_header.uMipMapsCount, false );
		sImage.aData.Resize( s_header.uDataSize, false );

		b_read &= pFile->ReadS( sImage.aMipMaps.ptr(),	(uint)sImage.aMipMaps.Size() );
		b_read &= pFile->ReadS( sImage.aData.ptr(),		(uint)sImage.aData.Size() );

		return b_read;
	}

//-------------------------------------------------------------------

}	// Loaders
}	// Engine